<template>
  <div style="position: relative">
    <el-alert
      v-show="!wsConnected"
      :title="'websocket '+$t('OFFLINE')"
      type="error"
      :close-text="$t('confirm')"
      @close="initSocket"
    />
    <div ref="gl" style="display: block"/>
    <div ref="camera2" style="position: absolute;right: 0;top: 0;width: 200px;height: 200px"/>
    <div ref="camera" style="position: absolute;width: 100%;height: 100%;left: 0;top: 0"/>
    <div style="margin-top: 10px">
      <el-row :gutter="20">
        <el-col :span="8">
          <span>X:</span><span>{{ angleX }}</span>
        </el-col>
        <el-col :span="8">
          <span>Y:</span><span>{{ angleY }}</span>
        </el-col>
        <el-col :span="8">
          <span>Z:</span><span>{{ angleZ }}</span>
        </el-col>
      </el-row>
    </div>
  </div>
</template>

<script>
import { listSensorWith } from '@/api/iot/iotDeviceSensorApi'
import * as THREE from 'three'
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader'
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'
import { leaveWebSocket, listenWebSocket } from '@/util/websocketTool'

export default {
  name: 'Gyroscope',
  props: {
    device: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
      // wsConnected: false,
      // ws: null,
      lastDeviceCore: null,
      angleX: 0,
      angleY: 0,
      angleZ: 0,
      scene: null,
      camera: null,
      renderer: null,
      mesh: null,
      camera2: null,
      cameraHelper: null,
      wsMonitor: {
        room: 'iot:device:' + this.device.deviceCore,
        onmessage: this.onSocketMsg
      }
    }
  },
  watch: {
    device: {
      deep: true,
      handler(val) {
        if (this.device.deviceCore) {
          this.listSensorWith()
          if (this.lastDeviceCore != null) {
            leaveWebSocket(this.wsMonitor)
            // this.ws.send(JSON.stringify({
            //   cmd: 'unBindRoom',
            //   param: 'iot:device:' + this.lastDeviceCore
            // }))
          }
          this.wsMonitor.room = 'iot:device:' + this.device.deviceCore
          listenWebSocket(this.wsMonitor)
          // this.ws.send(JSON.stringify({
          //   cmd: 'bindRoom',
          //   param: 'iot:device:' + this.device.deviceCore
          // }))
          this.lastDeviceCore = this.device.deviceCore
        }
      }
    }
  },
  beforeDestroy() {
    // this.ws.send(JSON.stringify({
    //   cmd: 'unBindRoom',
    //   param: 'iot:device:' + this.device.deviceCore
    // }))
    // this.ws.close()
    this.closeSocket()
  },
  mounted() {
    const that = this
    if (this.device.deviceCore != null) {
      this.lastDeviceCore = this.device.deviceCore
      this.listSensorWith()
    }
    // this.initSocket()
    this.wsMonitor.room = 'iot:device:' + this.device.deviceCore
    listenWebSocket(this.wsMonitor)
    /**
     * 场景
     * @type {Scene}
     */
    this.scene = new THREE.Scene()
    this.scene.background = new THREE.Color('#FFFFFF')

    /**
     * 摄像机
     * @type {PerspectiveCamera}
     */
    this.camera = new THREE.PerspectiveCamera(45, 2, 5, 1000)
    this.camera.position.set(0, 8, 10)
    const controls = new OrbitControls(this.camera, this.$refs.camera)
    controls.target.set(0, 5, 0)
    controls.update()

    this.cameraHelper = new THREE.CameraHelper(this.camera)
    this.scene.add(this.cameraHelper)

    /**
     * 摄像头2
     */
    this.camera2 = new THREE.PerspectiveCamera(
      30, 2, 0.1, 1000
    )
    this.camera2.position.set(80, 20, 10)
    this.camera2.lookAt(0, 5, 0)
    const controls2 = new OrbitControls(this.camera2, this.$refs.camera2)
    controls2.target.set(0, 5, 0)
    controls2.update()

    /**
     * 着色器
     * @type {WebGLRenderer}
     */
    this.renderer = new THREE.WebGLRenderer()
    this.renderer.setSize(this.$refs.gl.offsetWidth, window.innerHeight - 200)
    this.renderer.shadowMap.enabled = true
    this.$refs.gl.appendChild(this.renderer.domElement)

    /**
     * 灯光
     */

    {
      const color = 0xFFFFFF
      const intensity = 1
      const light = new THREE.DirectionalLight(color, intensity)
      light.castShadow = true
      light.position.set(0, 15, 10)
      light.target.position.set(0, 5, 0)
      this.scene.add(light)
      this.scene.add(light.target)

      const cameraHelper = new THREE.CameraHelper(light.shadow.camera)
      this.scene.add(cameraHelper)
    }

    const groundGeometry = new THREE.PlaneGeometry(50, 50)
    const groundMaterial = new THREE.MeshPhongMaterial({ color: 0xCC8866 })
    const groundMesh = new THREE.Mesh(groundGeometry, groundMaterial)
    groundMesh.rotation.x = Math.PI * -0.5
    groundMesh.receiveShadow = true
    this.scene.add(groundMesh)

    /**
     * 底板
     */
    {
      const planeSize = 50

      const loader = new THREE.TextureLoader()
      const texture = loader.load('https://threejsfundamentals.org/threejs/resources/images/checker.png')
      texture.wrapS = THREE.RepeatWrapping
      texture.wrapT = THREE.RepeatWrapping
      texture.magFilter = THREE.NearestFilter
      const repeats = planeSize / 2
      texture.repeat.set(repeats, repeats)

      const planeGeo = new THREE.PlaneGeometry(planeSize, planeSize)
      const planeMat = new THREE.MeshPhongMaterial({
        map: texture,
        side: THREE.DoubleSide
      })
      const mesh = new THREE.Mesh(planeGeo, planeMat)
      mesh.receiveShadow = true
      mesh.rotation.x = Math.PI * -0.5
      this.scene.add(mesh)
    }

    /**
     * 长方体
     * @type {BoxGeometry}
     */
    const geometry = new THREE.BoxGeometry(1, 1, 1)

    // const mats = []
    //
    // mats.push(new THREE.MeshBasicMaterial({ color: new THREE.Color(0x00ffff) }))
    // mats.push(new THREE.MeshBasicMaterial({ color: new THREE.Color(0xff00ff) }))
    // mats.push(new THREE.MeshPhongMaterial({ map: new THREE.CanvasTexture(this.getTextCanvas('ZTN')) }))
    // mats.push(new THREE.MeshBasicMaterial({ color: new THREE.Color(0xff0000) }))
    // mats.push(new THREE.MeshBasicMaterial({ color: new THREE.Color(0x0000ff) }))

    const mesh1 = new THREE.Mesh(geometry, new THREE.MeshBasicMaterial({ color: new THREE.Color(0x00ffff) }))
    const axes = new THREE.AxesHelper()
    axes.material.depthTest = false
    axes.renderOrder = 50
    mesh1.add(axes)
    mesh1.position.set(0, 0, 0)
    mesh1.castShadow = true
    mesh1.receiveShadow = true
    this.scene.add(mesh1)

    const loader = new GLTFLoader()

    loader.load('./gltf/scene.gltf', function(gltf) {
      gltf.w = 1500
      that.mesh = gltf.scene
      that.mesh.position.set(0, 5, 0)
      that.mesh.castShadow = true
      that.mesh.receiveShadow = true
      that.scene.add(that.mesh)
      that.render()
      that.$refs.camera.addEventListener('dblclick', that.onMouseDblclick, false)
    }, undefined, function(error) {
      console.error(error)
    })
  },
  methods: {
    closeSocket() {
      leaveWebSocket(this.wsMonitor)
    },
    onMouseDblclick(event) {
      // 获取 raycaster 和所有模型相交的数组，其中的元素按照距离排序，越近的越靠前
      var intersects = this.getIntersects(event)
      // 获取选中最近的 Mesh 对象
      if (intersects.length !== 0 && intersects[0].object instanceof THREE.Mesh) {
        const selectObject = intersects[0].object
        console.log(selectObject)
      } else {
        console.log('未选中 Mesh!')
      }
    },
    getIntersects(event) {
      event.preventDefault()
      const raycaster = new THREE.Raycaster()
      const mouse = new THREE.Vector3()
      mouse.x = 0
      mouse.y = 5
      mouse.z = 0
      console.log('mouse.x:' + mouse.x)
      console.log('mouse.y:' + mouse.y)
      raycaster.setFromCamera(this.camera, mouse)
      const intersects = raycaster.intersectObjects(this.scene.children)
      return intersects
    },
    setScissorForElement(elem) {
      const canvasRect = this.renderer.domElement.getBoundingClientRect()
      const elemRect = elem.getBoundingClientRect()

      // compute a canvas relative rectangle
      const right = Math.min(elemRect.right, canvasRect.right) - canvasRect.left
      const left = Math.max(0, elemRect.left - canvasRect.left)
      const bottom = Math.min(elemRect.bottom, canvasRect.bottom) - canvasRect.top
      const top = Math.max(0, elemRect.top - canvasRect.top)

      const width = Math.min(canvasRect.width, right - left)
      const height = Math.min(canvasRect.height, bottom - top)

      // setup the scissor to only render to that part of the canvas
      const positiveYUpBottom = canvasRect.height - bottom
      this.renderer.setScissor(left, positiveYUpBottom, width, height)
      this.renderer.setViewport(left, positiveYUpBottom, width, height)

      // return the aspect
      return width / height
    },
    getTextCanvas(text) {
      const width = 1000
      const height = 500
      const canvas = document.createElement('canvas')
      canvas.width = width
      canvas.height = height
      const ctx = canvas.getContext('2d')
      ctx.fillStyle = '#C3C3C3'
      ctx.fillRect(0, 0, width, height)
      ctx.font = 300 + 'px " bold'
      ctx.fillStyle = '#2891FF'
      ctx.textAlign = 'center'
      ctx.textBaseline = 'middle'
      ctx.fillText(text, width / 2, height / 2)
      return canvas
    },
    resizeRendererToDisplaySize(renderer) {
      const canvas = renderer.domElement
      const width = canvas.clientWidth
      const height = canvas.clientHeight
      const needResize = canvas.width !== width || canvas.height !== height
      if (needResize) {
        renderer.setSize(width, height, false)
      }
      return needResize
    },
    render() {
      const that = this
      that.resizeRendererToDisplaySize(that.renderer)
      // turn on the scissor
      that.renderer.setScissorTest(true)
      {
        const aspect = that.setScissorForElement(that.$refs.camera)

        // adjust the camera for this aspect
        that.camera.aspect = aspect
        that.camera.updateProjectionMatrix()
        that.cameraHelper.update()

        // don't draw the camera helper in the original view
        that.cameraHelper.visible = false
        that.scene.background.set(0x000000)
        // render
        that.renderer.render(that.scene, that.camera)
      }
      {
        const aspect = that.setScissorForElement(that.$refs.camera2)

        // adjust the camera for this aspect
        that.camera2.aspect = aspect
        that.camera2.updateProjectionMatrix()

        // draw the camera helper in the 2nd view
        that.cameraHelper.visible = true
        that.scene.background.set(0x000040)
        this.renderer.render(this.scene, that.camera2)
      }

      requestAnimationFrame(that.render)
    },
    setX(x) {
      this.angleX = -x / 180 * Math.PI
      this.mesh.rotation.x = this.angleX
    },
    setY(y) {
      this.angleZ = -y / 180 * Math.PI
      this.mesh.rotation.z = this.angleZ
    },
    setZ(z) {
      this.angleY = -z / 180 * Math.PI
      this.mesh.rotation.y = this.angleY
    },
    onSocketMsg(nowData) {
      if (nowData.type === 'deviceData') {
        const data = JSON.parse(nowData.msg)
        // console.log(data)
        if (data.deviceUpload) {
          if (data.deviceUpload.sensorList && data.deviceUpload.sensorList.length > 0) {
            data.deviceUpload.sensorList.forEach(sensor => {
              if (sensor.explainList && sensor.explainList.length > 0) {
                sensor.explainList.forEach(explain => {
                  if (explain.keyName === 'angleX') {
                    this.setX(explain.value)
                  } else if (explain.keyName === 'angleY') {
                    this.setY(explain.value)
                  } else if (explain.keyName === 'angleZ') {
                    this.setZ(explain.value)
                  }
                })
              }
            })
          }
        }
      }
    },
    // initSocket() {
    //   const that = this
    //   this.ws = newWebSocket()
    //   this.ws.onopen = () => {
    //     that.wsConnected = true
    //     that.ws.send(JSON.stringify({
    //       cmd: 'bindRoom',
    //       param: 'iot:device:' + that.device.deviceCore
    //     }))
    //   }
    //   this.ws.onerror = function(e) {
    //     that.wsConnected = false
    //     console.error('WebSocket is error' + e.code + ' ' + e.reason + ' ' + e.wasClean)
    //     that.ws.close()
    //     setTimeout(function() {
    //       that.initSocket()
    //     }, 10000)
    //   }
    //   this.ws.onclose = function(e) {
    //     that.wsConnected = false
    //     console.error('WebSocket is close' + e.code + ' ' + e.reason + ' ' + e.wasClean)
    //     setTimeout(function() {
    //       that.initSocket()
    //     }, 10000)
    //   }
    //   this.ws.onmessage = function(msg) {
    //     const nowData = JSON.parse(msg.data)
    //     if (nowData.type === 'deviceData') {
    //       const data = JSON.parse(nowData.msg)
    //       // console.log(data)
    //       if (data.deviceUpload) {
    //         if (data.deviceUpload.sensorList && data.deviceUpload.sensorList.length > 0) {
    //           data.deviceUpload.sensorList.forEach(sensor => {
    //             if (sensor.explainList && sensor.explainList.length > 0) {
    //               sensor.explainList.forEach(explain => {
    //                 if (explain.keyName === 'angleX') {
    //                   that.setX(explain.value)
    //                 } else if (explain.keyName === 'angleY') {
    //                   that.setY(explain.value)
    //                 } else if (explain.keyName === 'angleZ') {
    //                   that.setZ(explain.value)
    //                 }
    //               })
    //             }
    //           })
    //         }
    //       }
    //     }
    //   }
    // },
    listSensorWith() {
      listSensorWith({
        deviceCore: this.device.deviceCore,
        withExplain: true
      }).then(res => {
        const { code, data } = res
        if (code === '200') {
          for (const item of data) {
            if (item.explainList) {
              for (const explain of item.explainList) {
                if (explain.keyName === 'angleX') {
                  this.setX(explain.value)
                } else if (explain.keyName === 'angleY') {
                  this.setY(explain.value)
                } else if (explain.keyName === 'angleZ') {
                  this.setZ(explain.value)
                }
              }
            }
          }
        }
      })
    }
  }
}
</script>


